package com.ltmonitor.jt808.service.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.ltmonitor.dao.IAlarmConfigDao;
import com.ltmonitor.dao.IMapAreaDao;
import com.ltmonitor.entity.AlarmConfig;
import com.ltmonitor.entity.AlarmRecord;
import com.ltmonitor.entity.GPSRealData;
import com.ltmonitor.entity.MapArea;
import com.ltmonitor.jt808.service.IAlarmService;
import com.ltmonitor.jt808.service.IAreaAlarmService;
import com.ltmonitor.jt808.service.INewAlarmService;
import com.ltmonitor.service.IAlarmConfigService;
import com.ltmonitor.service.IAlarmRecordService;
import com.ltmonitor.service.ILocationService;
import com.ltmonitor.service.IMapAreaService;
import com.ltmonitor.service.IQueryService;
import com.ltmonitor.util.DateUtil;
import com.ltmonitor.util.StringUtil;

/**
 * 报警解析服务
 * 
 * @author DELL
 * 
 */
@Service("alarmService")
public class AlarmService implements IAlarmService {

	private static Logger logger = Logger.getLogger(AlarmService.class);
	private ConcurrentLinkedQueue<AlarmRecord> dataQueue = new ConcurrentLinkedQueue();

	public ConcurrentMap<String, GPSRealData> oldRealDataMap = new ConcurrentHashMap<String, GPSRealData>();

	@Autowired
	private IQueryService queryService;	

	private Thread processRealDataThread;

	/**
	 * 报警记录 map
	 */
	private ConcurrentHashMap<String, AlarmRecord> alarmMap = new ConcurrentHashMap<String, AlarmRecord>();
	private Boolean startAnalyze = true;

	@Autowired
	private IAreaAlarmService areaAlarmService;

	private HashMap<String, String> alarmStateMap = new HashMap<String, String>();
	private HashMap<String, String> statusMap = new HashMap<String, String>();

	@Autowired
	private INewAlarmService newAlarmService;

	@Autowired
	private IAlarmRecordService alarmRecordService;
	
	@Autowired
	private ILocationService locationService;

	@Autowired
	private IAlarmConfigService alarmConfigService;
	

	@Autowired
	private IMapAreaService mapAreaService;

	// private boolean parkingAlarmEnabled;

	private Map<String, AlarmConfig> alarmConfigMap = new HashMap<String, AlarmConfig>();

	public AlarmService() {

	}

	/**
	 * 启动报警解析服务的线程，不断的从队列中读取实时数据，解析出报警，生成报警记录和报警时长统计记录
	 */
	public void start() {
		this.getAlarmConfig();
		processRealDataThread = new Thread(new Runnable() {
			public void run() {
				processRealDataThreadFunc();
			}
		});
		processRealDataThread.start();

		try {
			String hql = "from AlarmRecord where status = ?";
			List ls = this.alarmRecordService.query(hql, AlarmRecord.STATUS_NEW);
			for (Object obj : ls) {
				AlarmRecord r = (AlarmRecord) obj;
				String key = r.getPlateNo() + "_" + r.getAlarmType() + "_"
						+ r.getAlarmSource();
				this.alarmMap.put(key, r);
			}
		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
		}
		this.getAlarmConfig();
		
		areaAlarmService.start();
	}

	@Override
	public void stopService() {
		startAnalyze = false;
		try {
			processRealDataThread.join(50000);
		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
		}
		// processRealDataThread.stop();
	}

	private void getAlarmConfig() {
		try {
			List<AlarmConfig> ls = this.alarmConfigService.query("from AlarmConfig where depId = 0");
			for (Object obj : ls) {
				AlarmConfig a = (AlarmConfig) obj;
				alarmConfigMap.put(a.getAlarmType() + "_" + a.getAlarmSource(),
						a);
			}
		} catch (Exception ex) {
			logger.error(ex.getMessage(),ex);
		}
	}

	public boolean isAlarmEnabled(String alarmType, String alarmSource) {
		String key = alarmType + "_" + alarmSource;
		if (alarmConfigMap.containsKey(key)) {
			AlarmConfig a = alarmConfigMap.get(key);
			return a.isEnabled();
		}
		return false;
	}

	public boolean isStatisticAlarm(String alarmType, String alarmSource) {
		String key = alarmType + "_" + alarmSource;
		if (alarmConfigMap.containsKey(key)) {
			AlarmConfig a = alarmConfigMap.get(key);
			return a.isStatisticEnabled();
		}
		return false;
	}

	/**
	 * 处理实时数据
	 */
	@Override
	public void processRealData(GPSRealData rd) {
		if (dataQueue.size() > 500) {
			logger.error("报警队列堵塞:" + dataQueue.size());
			// dataQueue.clear();
		}

		this.analyzeData(rd);
	}

	private void processRealDataThreadFunc() {
		int count = 0;
		while (startAnalyze) {
			try {
				AlarmRecord tm = dataQueue.poll();
				if (tm != null) {
					final List<AlarmRecord> msgList = new ArrayList<AlarmRecord>();
					while (tm != null) {
						msgList.add(tm);
						if (msgList.size() > 30)
							break;
						tm = dataQueue.poll();
					}
					if (msgList.size() > 0) {
						saveAlarmRecord(msgList);
					}
				}
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}

			if (count % 300 == 0) {
				getAlarmConfig();// 更新报警配置
				count = 0;
			}
			count++;
			try {
				Thread.sleep(100L);
			} catch (InterruptedException e1) {
			}
		}
	}

	/**
	 * 保存报警时长记录
	 * @param msgList
	 */
	private void saveAlarmRecord(List<AlarmRecord> msgList) {

		for (AlarmRecord r : msgList) {
			try {
				// analyzeData(msg);
				if (r.getStatus().equals(AlarmRecord.STATUS_NEW)) {
					this.alarmRecordService.save(r);
				} else {
					this.alarmRecordService.saveOrUpdate(r);
				}
			} catch (Exception ex) {
				logger.error(ex.getMessage(), ex);
			}
		}

	}

	private void analyzeData(GPSRealData rd) {

		try {
			String newStatus = rd.getStatus();
			String newAlarmState = rd.getAlarmState();
			// createChangeRecord(AlarmRecord.STATE_FROM_TERM, newStatus, rd);

			String oldStatus = this.statusMap.get(rd.getPlateNo());
			if (newStatus != null && newStatus.equals(oldStatus) == false) {
				createChangeRecord(AlarmRecord.STATE_FROM_TERM, newStatus, rd);
				statusMap.put(rd.getPlateNo(), newStatus);
			}

			String oldAlarmState = alarmStateMap.get(rd.getPlateNo());
			if (newAlarmState != null
					&& (newAlarmState.equals(oldAlarmState) == false || newAlarmState
							.indexOf("1") >= 0)) {
				createChangeRecord(AlarmRecord.ALARM_FROM_TERM, newAlarmState,
						rd);
				alarmStateMap.put(rd.getPlateNo(), newAlarmState);
			}

			//停车报警
			if (isAlarmEnabled(AlarmRecord.TYPE_PARKING,
					AlarmRecord.ALARM_FROM_PLATFORM)) {
				//车速为0视为停车
				String alarmState = rd.getVelocity() < 1 ? AlarmRecord.TURN_ON
						: AlarmRecord.TURN_OFF;
				if(alarmState.equals(AlarmRecord.TURN_ON))
				{
					this.newAlarmService.insertAlarm(AlarmRecord.ALARM_FROM_PLATFORM, AlarmRecord.TYPE_PARKING, rd);
				}
				analyzeAlarm(rd, AlarmRecord.TYPE_PARKING,
						AlarmRecord.ALARM_FROM_PLATFORM, alarmState);
			}

		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
		}

	}

	private void analyzeAlarm(GPSRealData rd, String alarmType,
			String alarmSource, String alarmState) {
		//是否计算报警时长
		if (isStatisticAlarm(alarmType, alarmSource) == false)
			return;
		String alarmKey = rd.getPlateNo() + "_" + alarmType + "_" + alarmSource;
		if (alarmState.equals(AlarmRecord.TURN_ON)) {
			// 是否报警开始
			if (alarmMap.containsKey(alarmKey) == false) {
				AlarmRecord item = new AlarmRecord(rd, alarmType, alarmSource);

				this.dataQueue.add(item);
				alarmMap.put(alarmKey, item);// 保留在内存中，用于判断报警状态是否变化
			} else {
				AlarmRecord item = alarmMap.get(alarmKey);
				if (item.getStatus().equals(AlarmRecord.STATUS_OLD)) {
					AlarmRecord itemNew = new AlarmRecord(rd, alarmType,
							alarmSource);
					this.dataQueue.add(itemNew);
					alarmMap.put(alarmKey, item);
				}
			}
		} else if (alarmState.equals(AlarmRecord.TURN_OFF)) {
			if (alarmMap.containsKey(alarmKey)) {
				AlarmRecord item = alarmMap.get(alarmKey); //
				// item.setOpen(false);
				alarmMap.remove(alarmKey);
				item.setStatus(AlarmRecord.STATUS_OLD);
				item.setEndTime(rd.getSendTime());
				item.setLatitude1(rd.getLatitude());
				item.setLongitude1(rd.getLongitude());
				double minutes = 0.1 * DateUtil.getSeconds(item.getStartTime(),
						item.getEndTime()) / 6;
				item.setTimeSpan(minutes);
				this.dataQueue.add(item);
			}
		}
	}

	/**
	 * 判断报警是否变化，如果发送变化则创建报警记录
	 * @param alarmSource
	 * @param oldStatus
	 * @param newStatus
	 * @param rd
	 */
	private void createChangeRecord(String alarmSource, String newStatus,
			GPSRealData rd) {

		char[] newChars = (char[]) newStatus.toCharArray();
		for (int m = 0; m < newChars.length; m++) {
			String alarmState = "" + newChars[m];
			int alarmId = 31 - m; //报警位
			String alarmType = "" + alarmId;
			if (alarmId == 0 && alarmSource.equals(AlarmRecord.STATE_FROM_TERM)) {
				// 如果是Acc状态位变化，则创建报警类型是Acc报警
				alarmType = AlarmRecord.TYPE_ACC;
				alarmSource = AlarmRecord.ALARM_FROM_TERM;
				alarmState = alarmState.equals(AlarmRecord.TURN_ON) ? AlarmRecord.TURN_OFF
						: AlarmRecord.TURN_ON;
			}
			// 判断是否是终端报警
			if (alarmSource.equals(AlarmRecord.ALARM_FROM_TERM)
					&& alarmState.equals(AlarmRecord.TURN_ON)
					&& this.isAlarmEnabled(alarmType, alarmSource)) {
				if (alarmId == 20) {
					alarmType = rd.getAreaAlarm() == 0 ? AlarmRecord.TYPE_IN_AREA
							: AlarmRecord.TYPE_CROSS_BORDER;
				} else if (alarmId == 21) {
					alarmType = rd.getAreaAlarm() == 0 ? AlarmRecord.TYPE_ON_ROUTE
							: AlarmRecord.TYPE_OFFSET_ROUTE;
				} else if (alarmId == 2) {
					String oldAlarmState = alarmStateMap.get(rd.getPlateNo());
					if (oldAlarmState != null) {
						String oldTiredState = oldAlarmState
								.substring(m, m + 1);
						if (AlarmRecord.TURN_OFF.equals(oldTiredState)
								|| rd.getTiredAlarmTime() == null) {
							rd.setTiredAlarmTime(rd.getSendTime());
						}
					}else
						rd.setTiredAlarmTime(rd.getSendTime());
				}
				if ((alarmId == 20 || alarmId == 21) && rd.getAreaId() > 0) {
					//String hql = "from MapArea where areaId = ? ";
					MapArea area =  this.mapAreaService.load(rd.getAreaId());
					if (area != null) {
						String location = alarmId == 20 ? "区域：" : "线路:";
						location += area.getName();
						rd.setLocation(location);
					}
				}
				newAlarmService.insertAlarm(alarmSource, alarmType, rd);
			}
			if (alarmSource.equals(AlarmRecord.ALARM_FROM_TERM)) {
				if (alarmId == 20) {
					alarmType = AlarmRecord.TYPE_IN_AREA;
					// 判断是否是进入围栏报警
					if (rd.getAreaAlarm() == 1)
						alarmState = AlarmRecord.TURN_OFF;
				} else if (alarmId == 21) {
					alarmType = AlarmRecord.TYPE_ON_ROUTE;
					if (rd.getAreaAlarm() == 1)
						alarmState = AlarmRecord.TURN_OFF;
				}
			}
			if (this.isAlarmEnabled(alarmType, alarmSource)) {
				this.analyzeAlarm(rd, alarmType, alarmSource, alarmState);
			}

		}

	}


	public AlarmRecord CreateRecord(String alarmSource, String alarmType,
			String alarmState, GPSRealData rd) {
		String hsql = "from AlarmRecord rec where startTime > ? and  rec.plateNo = ? and rec.status = ? and rec.alarmSource = ? and rec.alarmType = ?";
		
		Date startDate = DateUtil.getDate(new Date(), Calendar.DAY_OF_YEAR, -5);
		AlarmRecord sr = (AlarmRecord) alarmRecordService.find(
				hsql,
				new Object[] { startDate, rd.getPlateNo(),
						AlarmRecord.STATUS_NEW, alarmSource, alarmType });

		if (sr == null) {
			if (AlarmRecord.TURN_OFF.equals(alarmState))
				return null;

			sr = new AlarmRecord();

			if (rd.getAreaId() > 0) {
				MapArea ec = (MapArea) this.mapAreaService.load(rd.getAreaId());
				if (ec != null) {
					sr.setLocation(ec.getName());
				}

			}
			sr.setVehicleId(rd.getVehicleId());
			sr.setAlarmSource(alarmSource);
			sr.setPlateNo(rd.getPlateNo());
			sr.setStartTime(rd.getSendTime());
			sr.setStatus(AlarmRecord.STATUS_NEW);
			sr.setEndTime(new Date());
			sr.setLatitude(rd.getLatitude());
			sr.setLongitude(rd.getLongitude());
			String location = rd.getLocation();
			if (StringUtil.isNullOrEmpty(location))
				location = locationService.getLocation(sr.getLatitude(),
						sr.getLongitude());
			sr.setLocation(location);
			sr.setVelocity(rd.getVelocity());
			sr.setAlarmType(alarmType);

		} else {
			sr.setEndTime(new Date());
			double minutes = DateUtil.getSeconds(sr.getStartTime(),
					rd.getSendTime()) / 60;
			sr.setTimeSpan(minutes);// 计算出报警开始到结束的时间长度，以分钟为单位
			if (alarmState.equals(AlarmRecord.TURN_OFF)) {
				sr.setStatus(AlarmRecord.STATUS_OLD);
				sr.setEndTime(rd.getSendTime());

				sr.setLatitude1(rd.getLatitude());
				sr.setLongitude1(rd.getLongitude());
			} else
				return null;

		}

		sr.setAlarmSource(alarmSource);
		sr.setAlarmType(alarmType);
		return sr;
	}

}
